/*
 * GLLib.cpp
 *
 *	Implementation for a potpourri of OpenGL functionality and initialization
 *	Manages the GL namespace, in which all OpenGL functionality will reside
 *	If you get errors here, ignore them-- it still ought to compile.
 *
 *  Created on: May 2, 2013
 *      Author: zapmunk
 */

#include "headerstop.h"

#include "GLLib.h"
#include "GL_CALLBACKS.h"
#include "GL_EXTENSIONS.h"

using std::cout; using std::endl;

namespace GL {
	#define init(name, ret, args) name ## _func name = 0
	#define init_adapt(adapter)
	_GL_CALLBACKS
	_GL_EXTENSIONS
	#undef init
	#undef init_adapt
};

bool GL::initGL(SDL_Window* window, SDL_GLContext* context) {
	//SDL_GL_LoadLibrary(NULL);
	*context = SDL_GL_CreateContext(window);

	#define init(name, ret, args) if (!(name = (name ## _func)SDL_GL_GetProcAddress(#name))) {SDL_SetError("Failed to load " #name "!"); return false;}
	_GL_CALLBACKS
	#undef init

	//GL extensions must be manually loaded because complications. ;(

	//GL_EXT_framebuffer_object
	if (SDL_GL_GetProcAddress("glGenFramebuffers")) {
		//normal framebuffers, all is well

		if (!(glGenFramebuffers = (glGenFramebuffers_func)SDL_GL_GetProcAddress("glGenFramebuffers"))) {
			SDL_SetError("Failed to load glGenFramebuffers!");
			return false;
		}
		if (!(glDeleteFramebuffers = (glDeleteFramebuffers_func)SDL_GL_GetProcAddress("glDeleteFramebuffers"))) {
			SDL_SetError("Failed to load glDeleteFramebuffers!");
			return false;
		}
		if (!(glBindFramebuffer = (glBindFramebuffer_func)SDL_GL_GetProcAddress("glBindFramebuffer"))) {
			SDL_SetError("Failed to load glBindFramebuffer!");
			return false;
		}
		if (!(glFramebufferTexture = (glFramebufferTexture_func)SDL_GL_GetProcAddress("glFramebufferTexture"))) {
			SDL_SetError("Failed to load glFramebufferTexture!");
			return false;
		}
		if (!(glCheckFramebufferStatus = (glCheckFramebufferStatus_func)SDL_GL_GetProcAddress("glCheckFramebufferStatus"))) {
			SDL_SetError("Failed to load glCheckFramebufferStatus!");
			return false;
		}
	} else if (SDL_GL_GetProcAddress("glGenFramebuffersEXT")) {
		//GL_EXT_framebuffer_object framebuffers, COMPLICATIONS ;_;
		cout << "Using GL_EXT_framebuffer_object framebuffers!" << endl;

		if (!(glGenFramebuffersEXT = (glGenFramebuffersEXT_func)SDL_GL_GetProcAddress("glGenFramebuffersEXT"))) {
			SDL_SetError("Failed to load glGenFramebuffersEXT!");
			return false;
		}
		if (!(glDeleteFramebuffersEXT = (glDeleteFramebuffersEXT_func)SDL_GL_GetProcAddress("glDeleteFramebuffersEXT"))) {
			SDL_SetError("Failed to load glDeleteFramebuffersEXT!");
			return false;
		}
		if (!(glBindFramebufferEXT = (glBindFramebufferEXT_func)SDL_GL_GetProcAddress("glBindFramebufferEXT"))) {
			SDL_SetError("Failed to load glBindFramebufferEXT!");
			return false;
		}
		if (!(glFramebufferTexture2DEXT = (glFramebufferTexture2DEXT_func)SDL_GL_GetProcAddress("glFramebufferTexture2DEXT"))) {
			SDL_SetError("Failed to load glFramebufferTexture2DEXT!");
			return false;
		}
		if (!(glCheckFramebufferStatusEXT = (glCheckFramebufferStatusEXT_func)SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT"))) {
			SDL_SetError("Failed to load glCheckFramebufferStatusEXT!");
			return false;
		}

		//link originals to these
		glGenFramebuffers = glGenFramebuffersEXT;
		glDeleteFramebuffers = glDeleteFramebuffersEXT;
		glBindFramebuffer = glBindFramebufferEXT;
		glFramebufferTexture = (glFramebufferTexture_func)_adapt_glFramebufferTexture2DEXT;
		glCheckFramebufferStatus = glCheckFramebufferStatusEXT;
	} else {
		SDL_SetError("This device supports neither GL framebuffers nor GL_EXT_framebuffer_object!");
		return false;
	}

	return true;
}

#undef _GL_CALLBACKS
#undef _GL_EXTENSIONS

bool GL::drawTexture(const Texture& tex, uint x, uint y, GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
	SDL_Rect src = {0, 0, tex.w, tex.h};
	return GL::drawTexture(tex, src, x, y, r, g, b, a);
}
bool GL::drawTexture(const Texture& tex, const SDL_Rect& src, uint x, uint y, GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
	SDL_Rect dest = {x, y, src.w, src.h};
	return GL::drawTexture(tex, src, dest, r, g, b, a);
}
bool GL::drawTexture(const Texture& tex, const SDL_Rect& src, const SDL_Rect& dest, GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
	if (tex.isNull()) return false;
	GL::glBindTexture(GL_TEXTURE_2D, tex.tex);
	GL::glBegin(GL_TRIANGLES);
	GL::glColor4f(r, g, b, a);
	GL::glTexCoord2f(tex.modX(src.x), tex.modY(src.y)); GL::glVertex2i(dest.x, dest.y);
	GL::glTexCoord2f(tex.modX(src.x+src.w), tex.modY(src.y)); GL::glVertex2i(dest.x+dest.w, dest.y);
	GL::glTexCoord2f(tex.modX(src.x+src.w), tex.modY(src.y+src.h)); GL::glVertex2i(dest.x+dest.w, dest.y+dest.h);

	GL::glTexCoord2f(tex.modX(src.x), tex.modY(src.y)); GL::glVertex2i(dest.x, dest.y);
	GL::glTexCoord2f(tex.modX(src.x), tex.modY(src.y+src.h)); GL::glVertex2i(dest.x, dest.y+dest.h);
	GL::glTexCoord2f(tex.modX(src.x+src.w), tex.modY(src.y+src.h)); GL::glVertex2i(dest.x+dest.w, dest.y+dest.h);
	GL::glEnd();
	return true;
}
